颜值: <input class="prop" name="CHR" style="width:300px;" value="5" >(CHR)<br/>
智力: <input class="prop" name="INT" style="width:300px;" value="5" >(INT)<br/>
体质: <input class="prop" name="STR" style="width:300px;" value="5" >(STR)<br/>
家境: <input class="prop" name="MNY" style="width:300px;" value="5" >(MNY)<br/>
快乐: <input class="prop" name="SPR" style="width:300px;" value="5" >(SPR)<br/>
生命: <input class="prop" name="LIF" style="width:300px;" value="5" >(LIF)<br/>
天赋: <input class="prop" name="TLT" style="width:300px;" value="[5]" >(TLT)<br/>
事件: <input class="prop" name="EVT" style="width:300px;" value="[5]" >(EVT)<br/>
<br/><br/>
表达式：<input id="conditions" style="width:500px;"><br/><br/>
结果：<span id="result" style="color:red;"></span><br/><br/>
<button id="submit">&nbsp;测&nbsp;&nbsp;试&nbsp;</button><br/>

<script>
    const DEFAULT_PROP = {
        CHR: 5,      // 颜值 charm CHR
        INT: 5,      // 智力 intelligence INT
        STR: 5,      // 体质 strength STR
        MNY: 5,      // 家境 money MNY
        SPR: 5,      // 快乐 spirit SPR
        LIF: 5,      // 生命 life LIF
        TLT: [5],    // 天赋 talent TLT
        EVT: [5],    // 事件 event EVT
    };
    document.querySelector("#submit").onclick = function() {
        document.querySelectorAll('.prop').forEach(({name, value})=>DEFAULT_PROP[name] = JSON.parse(value));
        conditions = document.querySelector("#conditions").value;
        const result = check(conditions);
        document.querySelector("#result").textContent = result;
    }

    function getProp(prop) {
        switch(prop) {
            case 'CHR':
            case 'INT':
            case 'STR':
            case 'MNY':
            case 'SPR':
            case 'LIF':
            case 'TLT':
            case 'EVT': return DEFAULT_PROP[prop];
            default: return null;
        }
    }

    function check(condition) {
        const conditions = parseCondition(condition);
        return checkParsedCondition(conditions);
    }

    function checkParsedCondition(conditions) {
        if(!Array.isArray(conditions)) return checkLogic(conditions);
        if(conditions.length == 0) return true;
        if(conditions.length == 1) return checkParsedCondition(conditions[0]);

        let ret = checkParsedCondition(conditions[0]);
        for(let i=1; i<conditions.length; i+=2) {
            switch(conditions[i]) {
                case '&':
                    if(ret) ret = checkParsedCondition(conditions[i+1]);
                    break;
                case '|':
                    if(ret) return true;
                    ret = checkParsedCondition(conditions[i+1]);
                    break;
                default: return false;
            }
        }
        return ret;

    }

    function checkLogic(condition) {
        const length = condition.length;
        let i = condition.search(/[><\!\?=]/);

        const prop = condition.substring(0,i);
        const symbol = condition.substring(i, i+=(condition[i+1]=='='?2:1));
        const d = condition.substring(i, length);

        const propData = getProp(prop);
        const conditionData = d[0]=='['? JSON.parse(d): Number(d);

        switch(symbol) {
            case '>':  return propData >  conditionData;
            case '<':  return propData <  conditionData;
            case '>=': return propData >= conditionData;
            case '<=': return propData <= conditionData;
            case '=':
                if(Array.isArray(propData))
                    return propData.includes(conditionData);
                return propData == conditionData;
            case '!=':
                if(Array.isArray(propData))
                    return !propData.includes(conditionData);
                return propData == conditionData;
            case '?':
                if(Array.isArray(propData)) {
                    for(const p of propData)
                        if(conditionData.includes(p)) return true;
                    return false;
                }
                return conditionData.includes(propData);
            case '!':
                if(Array.isArray(propData)) {
                    for(const p of propData)
                        if(conditionData.includes(p)) return false;
                    return true;
                }
                return !conditionData.includes(propData);

            default: return false;
        }
    }

    function parseCondition(condition) {
        const conditions = [];
        const length = condition.length;
        const stack = [];
        stack.unshift(conditions);
        let cursor = 0;
        const catchString = i => {
            const str = condition.substring(cursor, i).trim();
            cursor = i;
            if(str) stack[0].push(str);
        };

        for(let i=0; i<length; i++) {
            switch(condition[i]) {
                case ' ': continue;

                case '(':
                    catchString(i);
                    cursor ++;
                    const sub = [];
                    stack[0].push(sub);
                    stack.unshift(sub);
                    break;

                case ')':
                    catchString(i);
                    cursor ++;
                    stack.shift();
                    break;

                case '|':
                case '&':
                    catchString(i);
                    catchString(i+1);
                    break;
                default: continue;
            }
        }

        catchString(length);

        return conditions;
    }

    // function debug(...conditions) {
    //     for(const condition of conditions)
    //         console.debug(check(condition), '\t', condition);
    // }
    //
    // debug(
    //     '(STR<2&MNY>3)|(MNY<2&CHR<2)',
    //     '(STR<2&MNY>3)',
    //     '(STR>2&MNY>3)',
    //     '((((STR>2&MNY>2))))',
    //     '((((STR>2&MNY>2)|(MNY<2&CHR<2))))',
    //     '((((STR>2&MNY>2)|(MNY<2&CHR<2)&(STR>2&MNY>3))))',
    //     '((((STR>2&MNY>2)|(MNY<2&CHR<2))&(STR>2&MNY>3)))',
    //     'EVT![1,2,3]',
    //     'EVT![1,2]',
    //     'EVT?[1,2,3]',
    //     'EVT?[1,2]',
    // );
</script>


